package cn.sunxyz.data.orm.impl;

import cn.sunxyz.data.infer.ex.SQLParserException;
import cn.sunxyz.data.infer.rule.NameRule;
import cn.sunxyz.data.orm.EntityQueryManger;
import cn.sunxyz.data.orm.IMethod;
import cn.sunxyz.data.orm.QueryProxy;
import cn.sunxyz.data.orm.domain.CrudDatabase;
import cn.sunxyz.data.orm.domain.MyDataBase;
import cn.sunxyz.data.orm.domain.SimpleDataBase;
import cn.sunxyz.data.orm.impl.DefaultEntityQueryManger;

import java.lang.reflect.*;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Created by yangrd on 2017/6/8.
 */
public class DefaultQueryProxy implements QueryProxy {


    @Override
    public <T extends MyDataBase> T getMapper(Class<T> dataBaseQueryInterfaceClass) {

        Type[] types = dataBaseQueryInterfaceClass.getGenericInterfaces();
        ParameterizedType parameterized = (ParameterizedType) types[0];
        Class<?> entityCls = (Class<?>) parameterized.getActualTypeArguments()[0];
        Class<?> idCls = (Class<?>) parameterized.getActualTypeArguments()[1];
        Class proxyClass = Proxy.getProxyClass(dataBaseQueryInterfaceClass.getClassLoader(), dataBaseQueryInterfaceClass);
        try {
            Constructor constructor = proxyClass.getConstructor(new Class[]{InvocationHandler.class});
            return (T) constructor.newInstance(new SimpleInterceptor(entityCls, idCls));//此处可以加入缓存
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    static class SimpleInterceptor implements InvocationHandler {

        private Class<?> entityCls;

        private CrudDatabase<Object, Object> crudDataCollection;

        public SimpleInterceptor(Class<?> entityCls, Class<?> idCls) {
            this.entityCls = entityCls;
            crudDataCollection = new SimpleDataBase(entityCls, idCls);
        }

        private static EntityQueryManger entityQueryManger = DefaultEntityQueryManger.getSingleton();

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            try {
//                System.out.println(method.getName() + ":=>>" + Arrays.asList(method.getParameterTypes()).stream().map(cls -> cls.getName()).collect(Collectors.joining()));
                Method method1 = crudDataCollection.getClass().getMethod(method.getName(), allClass(method.getParameterTypes()));
                return method1.invoke(crudDataCollection, args);
            } catch (NoSuchMethodException e) {
                //TODO 推断
                String methodName = method.getName();
                int index = methodName.indexOf("By");//TODO 目前暂不支持 findNameById这种语句 只支持findByName
                String namePrefix = methodName.substring(0, index).toUpperCase();
                if (NameRule.contains(namePrefix)) {
                    if (NameRule.isQuery(namePrefix)) {
                        if (method.getReturnType().isAssignableFrom(List.class)) {
                            return entityQueryManger.queryList(IMethod.Builder.of(method, entityCls), args);
                        } else {
                            return entityQueryManger.query(IMethod.Builder.of(method, entityCls), args);
                        }
                    } else {
                        return entityQueryManger.delete(IMethod.Builder.of(method, entityCls), args);
                    }
                } else {
                    throw new SQLParserException("The name does not conform to the specification");//名字不符合规范
                }
            }
        }

        private Class<?>[] allClass(Class<?>[] args) {
            if (Objects.isNull(args)) {
                return null;
            }
            List<Class<?>> classList = Stream.of(args).map(arg -> Object.class).collect(Collectors.toList());
            Class<?>[] classArray = new Class<?>[classList.size()];
            return classList.toArray(classArray);
        }
    }

}
